import React, { FC, useEffect, useRef, useState } from 'react';
import { DetailItem, HexStr, Section, SectionUUId, Summary } from "../../entity/Summary";
import { Spin, Tree } from "antd";
import { v4 as uuidv4 } from 'uuid';
import { DataNode } from 'rc-tree/lib/interface.d'
import "./index.css"
import {
  BoxTypes,
  Direction,
  DownIcon,
  DragDiv,
  DragDivProps,
  DragId,
  LeftIcon,
  RightIcon,
  UpIcon
} from "../../components/DragDiv";
import { useDrop, XYCoord } from "react-dnd";
import { CustomDragLayer } from '../../components/CustomDragLayer';
import RcTree from 'rc-tree';
import { TableArea } from "./TableArea";
import { ArrowIcon } from "./ArrowIcon";
import DrawScroll from '../../components/DrawScroll';
import RightMenu from "../../components/RightMenu";
import { pascalCase } from "change-case";
import { FilterHeight } from "./Filter";
import useGlobalStore from "../../store/UseGlobalStore";
import { useMount } from "ahooks";


export type HomeProps = { width: number, height: number }
const emptySection = { left: 0, right: 0 }
const dragWidth = 5;
const Home: FC<HomeProps> = (props) => {
  let treeRef = useRef<RcTree | null>(null);
  const { width, height } = props
  const { pcapResult, loading } = useGlobalStore()
  const [summary, setSummary] = useState<Summary[]>([]);
  const [clickIndex, setClickIndex] = useState(0);
  const [isFromCanvas, setIsFromCanvas] = useState(false);
  const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
  const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true);
  const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
  const [detailList, setDetailList] = useState<DetailItem[][]>([]);
  const [hexList, setHexList] = useState<any[]>([]);
  const [clickedDetail, setClickedDetail] = useState<DetailItem[]>([]);
  const [clickedHex, setClickedHex] = useState<any>(undefined);
  const [lineNumbers, setLineNumbers] = useState<string[]>([]);
  const [hexStr, setHexStr] = useState<HexStr[]>([]);
  const [highSection, setHighSection] = useState<Section>(emptySection);
  const [hoverSection, setHoverSection] = useState<Section>(emptySection);
  const [sections, setSections] = useState<Section[]>([]);
  const [sectionMap, setSectionMap] = useState<Map<string, SectionUUId>>(new Map<string, SectionUUId>());
  const [pos1, setPos1] = useState<XYCoord>({ x: width / 2, y: 0 });
  const [pos2, setPos2] = useState<XYCoord>({ x: 0, y: height / 2 });
  const [pos3, setPos3] = useState<XYCoord>({ x: height / 2, y: height / 4 });
  const [isChartFullScreen, setIsChartFullScreen] = useState(false);
  const [isTreeFullScreen, setIsTreeFullScreen] = useState(false);
  const [isLeftTreeFullScreen, setIsLeftTreeFullScreen] = useState(false);
  const [treeFlatList, setTreeFlatList] = useState<DetailItem[]>([]);
  const [sectionUUId, setSectionUUId] = useState<SectionUUId | undefined>(undefined);
  const [rightMenuPos, setRightMenuPos] = useState<XYCoord>({ x: 0, y: 0 });
  const [rightClickNode, setRightClickNode] = useState<DetailItem | undefined>(undefined);
  const [lastClickNode, setLastClickNode] = useState<DetailItem | undefined>(undefined);
  const [lastExpandNode, setLastExpandNode] = useState<DetailItem[]>([]);
  const [treeWidth, setTreeWidth] = useState(600);
  
  useEffect(() => {
    setPos1({ x: isChartFullScreen ? width - dragWidth : width / 2, y: 0 })
    setPos2({ x: 0, y: isTreeFullScreen ? 0 : height / 2 })
    setPos3({ x: isLeftTreeFullScreen ? width - dragWidth - pos1.x : (width - pos1.x - dragWidth) / 2, y: 0 })
  }, [isChartFullScreen, isTreeFullScreen, isLeftTreeFullScreen, width, height]);
  useEffect(() => {
    setHighSection(emptySection);
    setHoverSection(emptySection);
    let v = document.getElementsByClassName("ant-table-body")[0];
    if (v && isFromCanvas) {
      v.scrollTop = v.scrollHeight / summary.length * clickIndex - v.clientHeight / 2;
      setIsFromCanvas(false)
    }
  }, [clickIndex]);
  useEffect(() => {
    if (clickedDetail.length === 0) return
    let map = new Map<string, SectionUUId>();
    let list: DetailItem[] = [];
    const rec = (x: DetailItem, m: Map<string, SectionUUId>, list: DetailItem[]) => {
      list.push(x)
      if (typeof x.key !== "undefined" && typeof x.section !== "undefined") {
        m.set(x.section.left + "," + x.section.right, { section: x.section, key: x.key.toString() })
      }
      if (typeof x.field !== "undefined" && x.field.length > 0) {
        x.field.forEach(y => rec(y, m, list))
      }
    };
    clickedDetail.forEach((x, i) => {
      rec(x, map, list);
    })
    // 设置 tree 的宽度
    let number1 = Math.max(...list.map(x => x.title as JSX.Element).map(x => x.props.children.length));
    setTreeWidth(number1 * 7)
    setTreeFlatList(list)
    setSectionMap(map)
  }, [clickedDetail]);

  function extracted(find: DetailItem) {
    setTimeout(() => {
      treeScrollToNode(find.key.toString());
    }, 500, find)
  }

  useEffect(() => {
    if (lastClickNode) {
      // 记住上一次点击高亮的节点，切换包的时候 再次高亮
      let name = lastClickNode["@_name"];
      let find = treeFlatList.find(x => x["@_name"] === name);
      if (find) {
        extracted(find);
        setSelectedKeys([find.key])
        setHoverSection(emptySection)
        if (find.section) {
          setHighSection(find.section)
        }
        let expandkeys = treeFlatList.filter(x => lastExpandNode.map(l => l["@_name"]).includes(x["@_name"])).map(x => x.key);
        setExpandedKeys(expandkeys)
      }
    }
  }, [treeFlatList]);
  const recAddChildKeys = (node: DataNode, keys: React.Key[]) => {
    if (node.children) {
      keys.push(node.key)
      node.children.forEach(x => recAddChildKeys(x, keys))
    }
  }
  useEffect(() => {
    if (pcapResult) {
      const { summaryMsg, detailMsg, hexMsg } = pcapResult;
      // 解析 detailList list
      // console.log("detailMsg",detailMsg)
      let parse = JSON.parse(detailMsg);
      let packet = parse.pdml.packet;
      let isArray = Array.isArray(packet);
      let map: any;
      if (isArray) {
        map = packet.map((x: any) => x.proto);
      } else {
        map = [packet].map((x: any) => x.proto);
      }
      // console.log("map", map)
      let periodList = map.map((x: any) => x[1].field[6]['@_show']);
      setDetailList(map)
      // 解析 summary list
      let strings = summaryMsg.split("\n").filter(x => x.length > 0).map(x => x.split("\t")).map((x, i) => {
        const summary: Summary = {
          no: x[0],
          timeStr: x[1],
          source: x[2],
          arrow: x[3],
          destination: x[4],
          protocol: x[5],
          length: x[6],
          info: x[7],
          period: Math.abs(periodList[i])
        };
        return summary
      });
      setSummary(strings);
      // 解析 hexList list
      // console.log("hexMsg",hexMsg)
      let parse1 = JSON.parse(hexMsg);
      let layers = parse1.map((x: any) => x._source.layers);
      setHexList(layers)
      if (map.length > 0 && layers.length > 0) {
        //默认选中第一个 detailList
        let detailElement = map[0].slice(1);
        //默认选中第一个 hexList
        let detailHex = layers[0];
        setDefaultDetail(detailElement, detailHex);
        setDefaultHex(detailHex)
      }
    } else {
      setSummary([])
      setClickedDetail([])
      setHexStr([])
      setLineNumbers([])
    }
  }, [pcapResult]);
  const setDefaultDetail = (detailElement: DetailItem[], detailHex: any) => {
    let uniqueMap = new Map<string, Section>();
    let map = new Map<string, any[]>();
    recObj(detailHex, map, uniqueMap);
    let l: Section[] = [];
    let arr: Section[] = [];
    uniqueMap.forEach((value, key) => {
      l.push(value)
    })
    // console.log("l", l)
    l.sort((a, b) => {
      return a.right - b.right
    })
    let lastRight = l[0].right;
    arr.push(l[0]);
    l.forEach(item => {
      if (item.left >= lastRight) {
        arr.push(item)
        lastRight = item.right;
      }
    })
    // console.log("arr", arr)
    setSections(arr)
    const recField = (node: DetailItem) => {
      if (!node["@_showname"]) {
        if (node.field) {
          if (Array.isArray(node.field)) {
            const item: any = {
              "@_name": 'wuming',
              "@_showname": `${pascalCase(node["@_name"])} (${node.field[0]["@_size"]} bytes )`,
              field: node.field
            }
            detailElement[detailElement.length - 1] = item
            // console.log("item", item)
          } else {
            [node.field].forEach(x => recField(x))
          }
        }
      }
    }
    detailElement.forEach(x => recField(x))
    detailElement.forEach(iter(0, map))
    setClickedDetail(detailElement)
  }
  const setDefaultHex = (detailHex: any) => {
    let size = detailHex.frame_raw[2];
    let lineNumber = Math.ceil(size / 16);
    let numbers = Array.from(Array(lineNumber).keys()).map(x => {
      let number = x * 16;
      return number.toString(16).padStart(4, "0")
    });
    // 两个一组
    let strs: string[] = detailHex.frame_raw[0].split(/(?=(?:..)*$)/);
    let hexStrs = strs.map(x => ({ char: x, isHighlight: false }));
    setHexStr(hexStrs);
    setLineNumbers(numbers);
    setClickedHex(detailHex);
  }
  const onSelect = (selectedKeysValue: React.Key[], info: {
    selected: boolean;
    node: DataNode;
  }) => {
    setLastClickNode(info.node as DetailItem)
    const expandNodes = treeFlatList.filter(x => expandedKeys.includes(x.key));
    setLastExpandNode(expandNodes)
    setHoverSection(emptySection)
    setSelectedKeys(selectedKeysValue);
    if (info.selected) {
      const node = info.node as DetailItem
      if (node.section) {
        setHighSection(node.section)
      }
    }
  };
  //十六进制转ASCII码
  const hexCharCodeToStr = (hexCharCodeStr: string) => {
    const trimedStr = hexCharCodeStr.trim();
    const rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
    const len = rawStr.length;
    if (len % 2 !== 0) {
      alert("存在非法字符!");
      return "";
    }
    let curCharCode;
    const resultStrArr = [];
    for (let i = 0; i < len; i = i + 2) {
      curCharCode = parseInt(rawStr.substr(i, 2), 16);
      resultStrArr.push(String.fromCharCode(curCharCode));
    }
    const checkS = (str: string) => {
      // 判断字符串是否为数字和字母组合
      const zg = /^[0-9A-Za-z~!@#$%^&*()/\|,.<>?"'();:_+-=\[\]{}]*$/;
      if (!zg.test(str)) {
        return false;
      } else {
        return true;
      }
    }
    let s = resultStrArr.join("");
    if (checkS(s)) {
      return s;
    } else {
      return "•";
    }
    return s;
  };
  const recObj = (detailHex: any, map: Map<string, any[]>, uniqMap: Map<string, Section>) => {
    // detailHex 是一个对象 ，现在要递归她 把带数组的都 展开 组合成一个新的数组
    Object.keys(detailHex).forEach((key: string) => {
      let value = detailHex[key];
      if (Array.isArray(value)) { // 是数组就 加到 新数组里面 去
        map.set(key, value)
        let rangeItem: Section = { left: value[1], right: value[1] + value[2] }
        if (rangeItem.left < rangeItem.right) {
          uniqMap.set(rangeItem.left + "," + rangeItem.right, rangeItem)
        }
      } else {
        // 不是就递归
        recObj(value, map, uniqMap)
      }
    })
  };
  const iter = (level: number, map: Map<string, any[]>) => {
    return function(node: DetailItem) {
      node.key = uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
      node.title = <span data-id={node.key}>{node["@_showname"]}</span>
      if (typeof node["@_showname"] === "undefined") {
        if (typeof node["@_show"] !== "undefined") {
          node.title = <span data-id={node.key}>{node["@_show"]}</span>
        }
      }
      let key = node["@_name"] + "_raw";
      let val = map.get(key);
      if (typeof val !== "undefined") {
        if (val.length === 5) node.section = { left: val[1], right: val[1] + val[2] }
        if (val.length === 4) node.section = { left: val[0], right: val[0] + val[1] }
      }
      if (node.field) {
        if (Array.isArray(node.field)) {
          node.children = node.field;
          node.field.forEach(iter(level + 1, map));
        } else {
          [node.field].forEach(iter(level + 1, map));
          node.children = [node.field];
        }
      } else {
        node.children = []
      }
    };
  };
  const hexOnClick = (i: number) => () => {
    let section = sections.find(x => x.left <= i && x.right >= i);
    if (section) {
      setHighSection(section);
      setHoverSection(emptySection)
      let pos = section.left + "," + section.right;
      let val = sectionMap.get(pos);
      if (typeof val !== "undefined") {
        let node = treeFlatList.find(x => x.key === val!.key);
        if (node) {
          setLastClickNode(node);
          const expandNodes = treeFlatList.filter(x => [val!.key, ...expandedKeys].includes(x.key));
          setLastExpandNode(expandNodes)
        }
        setSelectedKeys([val.key])
        setExpandedKeys([val.key, ...expandedKeys])
        setAutoExpandParent(true);
        setSectionUUId(val)
      }
    }
  };

  function treeScrollToNode(key: string) {
    let pDiv = document.getElementById('treeParent'); //找到树外面包裹的一层div 他是负责滚动的
    if (!pDiv) return
    let childDiv = pDiv.children[0] as HTMLElement; //找到树的element
    let span = childDiv.querySelector(`[data-id="${key}"]`) as HTMLElement;
    if (span) { // 根据key 找到span
      let spanTop = span.getBoundingClientRect().top;
      let pDivTop = pDiv.getBoundingClientRect().top;
      span.scrollIntoView(true)
      // pDiv.scrollTo({top: spanTop - pDivTop})
    }
  }

  useEffect(() => {
    setTimeout(() => { // 因为expandedKeys 变化的时候 ,树高度在变化的时候 是异步的 所以要等一下,不然找到的数据不一致
      if (!sectionUUId) return
      let detailItems = treeFlatList.find(x => x.key === (sectionUUId)!.key);
      if (detailItems) {
        treeScrollToNode(sectionUUId.key);
      }
    }, 500)
  }, [sectionUUId]);
  const hexOnMouseEnter = (i: number) => () => {
    let section = sections.find(x => x.left <= i && x.right >= i);
    if (section) {
      setHoverSection(section);
    }
  };
  const hexClass = (i: number, isHex: boolean) => `item ${isHex ? 'item-hex' : 'item-ascii'} ${i >= highSection.left && i < highSection.right && 'item-highlight'} ${i >= hoverSection.left && i < hoverSection.right && 'item-hover'}`;
  const [, drop] = useDrop(
    () => ({
      accept: BoxTypes.BOX,
      drop(item: DragDivProps, monitor) {
        const delta = monitor.getDifferenceFromInitialOffset() as {
          x: number
          y: number
        }
        let left = Math.round(item.left + delta.x);
        let top = Math.round(item.top + delta.y);
        switch (item.id) {
          case DragId.pos1:
            setPos1({ x: left, y: top });
            break;
          case DragId.pos2:
            setPos2({ x: left, y: top });
            break;
          case DragId.pos3:
            setPos3({ x: left, y: top });
            break;
        }
        return undefined
      },
    }),
    [pos1, pos2, pos3],
  )
  return (
    <Spin spinning={loading}>
      <div style={{ display: "flex", height: `calc(100vh - ${FilterHeight}px)` }} ref={drop}>
        <DrawScroll summaryIndex={clickIndex} summaries={summary} width={pos1.x - 25} height={height}
          onClick={(index: number) => {
            setIsFromCanvas(true)
            let detailElement = detailList[index].slice(1);
            let detailHex = hexList[index];
            setClickIndex(index);
            setDefaultDetail(detailElement, detailHex)
            setDefaultHex(detailHex)
          }} pos1={pos1} />
        <DragDiv size={{ width: dragWidth, height: height }} id={DragId.pos1} direction={Direction.Vertical} left={pos1.x}
          top={pos1.y}
          icon={
            <ArrowIcon extStyle={{
              height: 30,
              transform: `translateX(-${dragWidth}px)`,
            }} setBool={setIsChartFullScreen} bool={isChartFullScreen} icon1={LeftIcon} icon2={RightIcon} />
          } />
        <CustomDragLayer pos2={pos2} pos3={pos3} pos1={pos1} />
        {!isChartFullScreen && <div style={{ display: 'flex', flexDirection: 'column', flex: 1, overflow: 'hidden', }}>
          <TableArea pos2={pos2} width={width} pos1={pos1} clickIndex={clickIndex} summary={summary}
            onRowClick={index => {
              let detailElement = detailList[index].slice(1);
              let detailHex = hexList[index];
              setClickIndex(index);
              setDefaultDetail(detailElement, detailHex)
              setDefaultHex(detailHex)
            }} />
          <DragDiv size={{ width: width - pos1.x, height: dragWidth }} id={DragId.pos2} direction={Direction.Horizontal}
            icon={
              <ArrowIcon icon1={DownIcon} icon2={UpIcon}
                extStyle={{ width: 30, transform: `translateY(${dragWidth}px)`, zIndex: 2, }}
                setBool={setIsTreeFullScreen} bool={isTreeFullScreen} />
            }
            left={pos2.x}
            top={pos2.y} />
          <div style={{ display: "flex", flex: 1, overflow: 'hidden', background: 'white', zIndex: 1 }}>
            <RightMenu node={rightClickNode} x={rightMenuPos.x} y={rightMenuPos.y} onFlatAll={() => {
              setExpandedKeys(treeFlatList.map(x => x.key))
              setRightMenuPos({ x: 0, y: 0 })
            }} onCollapseAll={() => {
              setExpandedKeys([])
              setRightMenuPos({ x: 0, y: 0 })
            }} onFlatOne={(node) => {
              if (node) {
                let keys: React.Key[] = [node.key]
                recAddChildKeys(node, keys)
                setExpandedKeys([...expandedKeys, ...keys])
              }
              setRightMenuPos({ x: 0, y: 0 })
            }} onCollapseOne={node => {
              if (node) {
                let keys: React.Key[] = [node.key]
                recAddChildKeys(node, keys)
                let filter = expandedKeys.filter(x => !keys.includes(x));
                setExpandedKeys(filter)
              }
              setRightMenuPos({ x: 0, y: 0 })
            }} />
            <div id={'treeParent'}
              style={{ width: pos3.x, border: '1px solid #eee', boxSizing: 'border-box', overflow: 'auto' }}>
              <Tree
                style={{ width: treeWidth }}
                ref={treeRef}
                onSelect={onSelect}
                selectedKeys={selectedKeys}
                treeData={clickedDetail}
                autoExpandParent={autoExpandParent}
                expandedKeys={expandedKeys}
                onExpand={(expandedKeysValue, { expanded, node }) => {
                  setRightMenuPos({ x: 0, y: 0 })
                  // 如果不设置 autoExpandParent 为 false，如果子级展开，则父级不能折叠。或者，您可以删除所有展开的子键。
                  setExpandedKeys(expandedKeysValue);
                  setAutoExpandParent(false);
                }}
                onClick={() => {
                  setRightMenuPos({ x: 0, y: 0 });
                }}
                onRightClick={(info) => {
                  setRightMenuPos({ x: 0, y: 0 })
                  setTimeout(() => {
                    setRightMenuPos({ x: info.event.clientX, y: info.event.clientY })
                    setRightClickNode(info.node as DataNode as DetailItem)
                  }, 100)
                }}
              />
            </div>
            <DragDiv size={{ width: dragWidth, height: height - pos2.y }} id={DragId.pos3}
              direction={Direction.Vertical}
              icon={
                <ArrowIcon icon1={LeftIcon} icon2={RightIcon} extStyle={{
                  height: 30,
                  transform: `translateX(-${dragWidth}px)`,
                }} setBool={setIsLeftTreeFullScreen} bool={isLeftTreeFullScreen} />
              }
              left={pos3.x}
              top={pos3.y} />
            <div style={{ flex: 1, border: '1px solid #eee', overflow: 'auto', boxSizing: 'border-box' }}>
              {clickedHex &&
                <div style={{
                  width: 500,
                  display: "flex",
                  overflowX: 'auto',
                }}>
                  {/* 16 进制行号*/}
                  <div style={{ width: 40 }}>{lineNumbers.map(x => <div key={x}><span
                    style={{
                      background: "#d9d9d9",
                      fontFamily: 'Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace'
                    }}>{x}</span>
                  </div>)}</div>
                  {/* 左边16进制 */}
                  <div style={{
                    display: "flex",
                    flexWrap: "wrap",
                    justifyContent: "flex-start",
                    width: 350
                  }}>
                    {hexStr.map((x, i) =>
                      <div key={i}
                        onMouseEnter={hexOnMouseEnter(i)}
                        onClick={hexOnClick(i)}
                        className={hexClass(i, true)}>
                        {x.char}
                      </div>)}
                  </div>
                  {/* 右边 ascii 码 */}
                  <div style={{
                    display: "flex",
                    flexWrap: "wrap",
                    justifyContent: "flex-start",
                    width: 150,
                    marginLeft: 10
                  }}>
                    {hexStr.map((x, i) =>
                      <div key={i}
                        onMouseEnter={hexOnMouseEnter(i)}
                        onClick={hexOnClick(i)}
                        className={hexClass(i, false)}>
                        {hexCharCodeToStr(x.char)}
                      </div>)}
                  </div>
                </div>
              }
            </div>
          </div>
        </div>}
      </div>
    </Spin>
  );
};
export default Home;
